package com.yeskery.nut.extend.mcp.server.method;

import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.bind.BindObject;
import com.yeskery.nut.bind.FitValueHelper;
import com.yeskery.nut.core.*;
import com.yeskery.nut.extend.mcp.annotation.McpResourceProperty;
import com.yeskery.nut.extend.mcp.core.Constants;
import com.yeskery.nut.extend.mcp.core.JsonError;
import com.yeskery.nut.extend.mcp.core.JsonMessage;
import com.yeskery.nut.extend.mcp.core.JsonResponse;
import com.yeskery.nut.extend.mcp.core.schema.ResourceContentsSchema;
import com.yeskery.nut.extend.mcp.core.schema.ResourceSchema;
import com.yeskery.nut.extend.mcp.server.McpException;
import com.yeskery.nut.extend.mcp.server.McpResourceMetadata;
import com.yeskery.nut.extend.mcp.server.McpServerCapability;
import com.yeskery.nut.extend.mcp.server.McpServerSession;
import com.yeskery.nut.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

/**
 * 资源读取方法处理器
 * @author sprout
 * 2025-04-29 14:31
 */
public class ResourcesReadMethodHandler extends BaseServerMethodHandler {
    /**
     * 构造基础服务方法处理器
     *
     * @param applicationContext  应用上下文
     * @param mcpServerCapability 服务能力
     */
    public ResourcesReadMethodHandler(ApplicationContext applicationContext, McpServerCapability mcpServerCapability) {
        super(applicationContext, mcpServerCapability);
    }

    @Override
    protected void handleJsonResponse(McpServerSession serverSession, JsonMessage jsonMessage, JsonResponse jsonResponse) {
        ResourceSchema.ReadResourceRequest readResourceRequest = getJsonRequestObject(jsonMessage, ResourceSchema.ReadResourceRequest.class);
        String uri = readResourceRequest.getUri();
        if (StringUtils.isBlank(uri)) {
            throw new McpException(new JsonError(ResponseCode.BAD_REQUEST.getCode(), "Parameter [" + uri + "] is required."));
        }
        List<McpResourceMetadata> resourcesMetadata = getMcpServerCapability().getResourcesMetadata();
        List<McpResourceMetadata> resourceMetadataList = resourcesMetadata.stream().filter(McpResourceMetadata::isResource).collect(Collectors.toList());
        for (McpResourceMetadata mcpResourceMetadata : resourceMetadataList) {
            if (mcpResourceMetadata.getPathMetadata().getOriginalPath().equals(uri)) {
                responseResourceData(uri, mcpResourceMetadata, jsonResponse);
                return;
            }
        }
        List<McpResourceMetadata> resourceTemplateMetadataList = resourcesMetadata.stream().filter(r -> !r.isResource()).collect(Collectors.toList());
        for (McpResourceMetadata mcpResourceMetadata : resourceTemplateMetadataList) {
            PathMetadata pathMetadata = mcpResourceMetadata.getPathMetadata();
            DynamicRegisterMetadata dynamicResourceMetadata = new DynamicRegisterMetadata(
                    Arrays.stream(pathMetadata.getParamNameAndPatterns()).map(KeyAndValue::getKey).toArray(String[]::new),
                    new PathMatcher(pathMetadata.getPathPattern()));
            Map<String, String> pathParameterMap = new HashMap<>();
            if (dynamicResourceMetadata.getPathMatcher().match(uri)) {
                Matcher matcher = dynamicResourceMetadata.getPathMatcher().getMatcher();
                for (int i = 0; i < dynamicResourceMetadata.getPathParameterNames().length; i++) {
                    String key = dynamicResourceMetadata.getPathParameterNames()[i];
                    String value = matcher.group(i + 1);
                    pathParameterMap.put(key, value);
                }
                responseResourceTemplateData(uri, mcpResourceMetadata, pathParameterMap, jsonResponse);
                return;
            }
        }
        throw new McpException(new JsonError(ResponseCode.NOT_FOUND.getCode(), "Resource [" + uri + "] not found."));
    }

    /**
     * 响应资源数据
     * @param uri 资源uri
     * @param resourceMetadata 资源元数据
     * @param jsonResponse 响应对象
     */
    private void responseResourceData(String uri, McpResourceMetadata resourceMetadata, JsonResponse jsonResponse) {
        String data = resourceMetadata.getData();
        if (!StringUtils.isBlank(data)) {
            boolean isTextContent = resourceMetadata.getTextContent();
            ResourceContentsSchema.ResourceContents contents;
            ResourceSchema.BaseResource resource = resourceMetadata.getResource().getResource();
            if (isTextContent) {
                ResourceContentsSchema.TextResourceContents textResourceContents = new ResourceContentsSchema.TextResourceContents();
                textResourceContents.setUri(uri);
                textResourceContents.setMimeType(resource.getMimeType());
                textResourceContents.setText(data);
                contents = textResourceContents;
            } else {
                ResourceContentsSchema.BlobResourceContents blobResourceContents = new ResourceContentsSchema.BlobResourceContents();
                blobResourceContents.setUri(uri);
                blobResourceContents.setMimeType(resource.getMimeType());
                blobResourceContents.setBlob(data);
                contents = blobResourceContents;
            }
            ResourceSchema.ReadResourceResult readResourceResult = new ResourceSchema.ReadResourceResult();
            readResourceResult.setContents(contents);
            jsonResponse.setResult(readResourceResult);
        }
        responseMethodResource(resourceMetadata.getMethod(), resourceMetadata.getBean(), new Object[0], uri, resourceMetadata, jsonResponse);
    }

    /**
     * 响应资源模板数据
     * @param uri 模板uri
     * @param resourceMetadata 模板元数据
     * @param pathParameterMap 路径参数
     * @param jsonResponse 响应对象
     */
    private void responseResourceTemplateData(String uri, McpResourceMetadata resourceMetadata, Map<String, String> pathParameterMap, JsonResponse jsonResponse) {
        Method method = resourceMetadata.getMethod();
        if (method == null) {
            ResourceSchema.Resource resource = new ResourceSchema.Resource();
            resource.setUri(uri);
            throw new McpException(new JsonError(Constants.RESOURCE_NOT_FOUND, "Resource [" + uri + "] not found.", resource));
        }
        Parameter[] parameters = method.getParameters();
        Object[] parameterValues = new Object[parameters.length];
        FitValueHelper fitValueHelper = FitValueHelper.getInstance();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            McpResourceProperty mcpResourceProperty = parameter.getAnnotation(McpResourceProperty.class);
            String name = mcpResourceProperty == null ? parameter.getName() : mcpResourceProperty.value();
            String value = pathParameterMap.get(name);
            if (value == null) {
                parameterValues[i] = null;
            } else {
                BindObject bindObject = fitValueHelper.getFitParamValue(name, value, parameter.getType());
                parameterValues[i] = bindObject.getData();
            }
        }
        responseMethodResource(method, resourceMetadata.getBean(), parameterValues, uri, resourceMetadata, jsonResponse);
    }

    /**
     * 响应方法资源
     * @param method 方法
     * @param object 对象
     * @param parameterValues 参数值
     * @param uri 模板uri
     * @param resourceMetadata 模板元数据
     * @param jsonResponse 响应对象
     */
    private void responseMethodResource(Method method, Object object, Object[] parameterValues, String uri,
                                        McpResourceMetadata resourceMetadata, JsonResponse jsonResponse) {
        Object invokeResult;
        try {
            invokeResult = method.invoke(object, parameterValues);
        } catch (Exception e) {
            throw new McpException(new JsonError(ResponseCode.INTERNAL_SERVER_ERROR.getCode(), "Resource [" + uri + "] read failure."));
        }
        if (invokeResult == null) {
            throw new McpException(new JsonError(ResponseCode.NOT_FOUND.getCode(), "Resource [" + uri + "] not found."));
        }
        ResourceContentsSchema.ResourceContents contents;
        ResourceSchema.BaseResource resource = resourceMetadata.getResource().getResource();
        if (invokeResult instanceof byte[]) {
            ResourceContentsSchema.BlobResourceContents blobResourceContents = new ResourceContentsSchema.BlobResourceContents();
            blobResourceContents.setUri(uri);
            blobResourceContents.setMimeType(resource.getMimeType());
            blobResourceContents.setBlob(new String(Base64.getEncoder().encode((byte[]) invokeResult), StandardCharsets.UTF_8));
            contents = blobResourceContents;
        } else {
            ResourceContentsSchema.TextResourceContents textResourceContents = new ResourceContentsSchema.TextResourceContents();
            textResourceContents.setUri(uri);
            textResourceContents.setMimeType(resource.getMimeType());
            if (invokeResult instanceof String) {
                textResourceContents.setText((String) invokeResult);
            } else {
                textResourceContents.setText(getResponsiveConvert().convertTo(invokeResult));
            }
            contents = textResourceContents;
        }
        ResourceSchema.ReadResourceResult readResourceResult = new ResourceSchema.ReadResourceResult();
        readResourceResult.setContents(contents);
        jsonResponse.setResult(readResourceResult);
    }
}
